<?php
/**
*     translate msg id: Yz::t() which calls Yz_I18N::translate
*                       Yz::tp() -> translatePlural
*
*   Installed languages settings in config:
*     #code:    #native name
*     en:       English
*     es:       Espanol
*     et:       Eesti
*     etc
*/
class Yz_I18N_Default
    extends Yz_Class
    implements Yz_I18N_Interface
{

    protected $instances = array();

    protected $language;

    protected $messages = array();
    protected static $globalMessages = array();


    protected static $installedLanguages = array( 'ru'=>'Русский', 'en'=>'English' );
    protected static $currentLanguage;
    protected static $instance;


    /**************************
     *      Public methods    *
     **************************/


    /********************************
     *      Static methods          *
     ********************************/


    /** Singleton by default
     *  @return Yz_Module_Interface&
     */
    public static function getInstance()
    {
        debug();

        $i =& self::$instance;
        if (null === $i) {
            $i = new self();
        }
        return $i;
    }

    /**
     *  @return string
     */
    public static function getCurrentLanguage()
    {
        debug();

        return self::$currentLanguage;
    } // getCurrentLanguage


    /** set the current language for all i18n reference, once per application
     *  @return void
     */
    public static function setCurrentLanguage( $code )
    {
        debug();

        // always look up the code in Yz_I18N_Default
        if( ! self::isInstalledLanguage($code) ) {

            // XXX code can be not a string
            throw new Yz_Exception( "no such language installed: '$code'" );
        }
        self::$currentLanguage = $code;
        return;
    } // setCurrentLanguage

    /**
     *  get list of installed languages
     *  @return array
     */
    public static function getInstalledLanguages()
    {
        debug();
        return self::$installedLanguages;
    }

    /**
     *  test whether a language/locale is installed
     *  @param string language code
     *  @return bool
     */
    public static function isInstalledLanguage( $lang )
    {
        debug();
        assert('is_string($lang)');
        // always look up the code in Yz_I18N_Default
        return array_key_exists( $lang, self::$installedLanguages );
    }


    /** get the i18n object for a language
     *  @return I18N_Interface
     */
    public function selectLanguage( $lang )
    {
        debug(get_called_class(). ':' .$lang);
        assert('is_string($lang)');
        $i =& $this->instances;
        if (! array_key_exists($lang, $i)) {
            $i[$lang] = new static($lang);
        }
        return $i[$lang];
    }



    /********************************
     *      Instance methods        *
     ********************************/

    /** I18N's constructor requires module
     *  @param Yz_Module_Interface $module
     */
    public function __construct( $language=null )
    {
        debug();
        self::$currentLanguage
            or self::init();
        $this->language = $language ?: self::$currentLanguage;
        debug($this->language);
        $ns = $this->namespaceName();
        $this->messages = $this->loadGlobalMessages($this->language);
        $class = $this;
        while ('' !== $ns) {
            $this->loadMessages($ns, $this->language);
            $class = get_parent_class($class);
            $ns = $class::namespaceName();
        }
    }

    /** get the language code of the instance
     *  @return string
     */
    public function getLanguage()
    {
        return $this->language;
    }

    /** fetch message using the current language and module
     *   @param string message
     *   @return string : a found message or the parameter itself
     */
    public function translate( $msg )
    {
        $msg = Yz_String::testNotEmpty($msg);
        return $this->defaultGetter( $msg );
    }

    /** fetch message using the current language and module
     *   @param string message
     *   @return string : a found message or the parameter itself
     */
    public function translateToLang( $msg, $lang )
    {
        return $this
            ->selectLanguage( $lang )
            ->translate($msg)
        ;
    }

    /** fetch message using the current language and module
     *   @param string message
     *   @param array args
     *   @return string : a found message or the parameter itself
     */
    public function translateWithParams( $msg, $args )
    {
        $msg = Yz_String::testNotEmpty($msg);
        $msg = $this->defaultGetter( $msg );
        return Yz_String::replaceVars($msg, $args);
    }

    /** setting vars is ignored / throwing ?
     *   @param ?
     *   @return ?
     *  @throws Yz_Exception_NotImplemented
     */
    public function offsetSet( $key, $value )
    {
        throw new Yz_Exception_NotImplemented();
    }




    /***********************
    *   Protected methods  *
    ************************/


    /** fetch message using the current language and module
     *  @param string message
     *  @return string : a found message or the argument itself
     */
    protected function defaultGetter( $msg )
    {
        if ($this->indexExists($msg)) {
            return $this->messages[$msg];
        }
        return $msg;
    }

    /** check if message exists
     *   @param string message
     *   @return bool
     */
    protected function indexExists( $msg )
    {
        return array_key_exists( $msg, $this->messages );
    }


    /** load messages for the instance
     *  @return this
     */
    protected function loadMessages( $ns, $lang )
    {
        debug();
        $fileName = strtr($ns, '\\', '/') . '/lang/' . $lang . '.php';
        $messages = $this->loadMessagesFromFile($fileName)
            and $this->messages += $messages;

        return $this;
    }


    /**
     *  load and return global (application-wide) messages
     *      load 2 files: LIB/$NS/lang/$lang.php and LIB/Yz/lang/$lang.php
     *      the first one overrides the second
     *  @return array
     */
    protected static function loadGlobalMessages( $lang )
    {
        debug();
        if (array_key_exists($lang, self::$globalMessages)) {
            $messages = self::$globalMessages[$lang];
        } else {
            $site = Yz::getAppNamespace();
            $fileName = "{$site}/lang/{$lang}.php";
            $messages = self::loadMessagesFromFile($fileName)
                or $messages = array();
            $fileName = 'Yz/lang/' . $lang . '.php';
            $messages2 = self::loadMessagesFromFile($fileName)
                and $messages += $messages2;
            self::$globalMessages[$lang] = $messages;
        }

        return $messages;
    }


    /** fetch list of messages from file
     *  @param string $filename
     *  @return array|null
     */
    protected static function loadMessagesFromFile($fileName)
    {
        $file = new Yz_File( $fileName, YZ_LIB );
        debug( "Loading i18n from file '$file'" );
        if ( $file->isFile() ) {
            // the included file is expected to return $messages[] =  ...
            $messages = include( "{$file}" );
            if (! is_array($messages)) {
                debug("BAD l10n file, messages must be an array");
                throw new Yz_I18N_Exception;
            }
            debug("Loaded messages: " . count($messages));
            return $messages;
        }
        return null;
    }

    /**
     *  gets called from the basic class only
     *
     */
    private static function init()
    {
        debug();
        $config = Yz::getConfig()
            ->fetchSection( 'i18n' );
        if( null === $config ) {
            $installedLang = self::$installedLanguages;
            $defaultLang = key( $installedLang );

        } else {

            $installedLang = $config->fetchSection( 'installed-languages' );

            if ( null === $installedLang || $installedLang->isEmpty() ) {
                //TODO:
                throw new Yz_I18N_Exception( "i18n section present but empty" );
            }

            self::$installedLanguages = $installedLang->toArray();

            $defaultLang = $config->fetchString( 'default-language' );

            if( null === $defaultLang ) {
                $defaultLang = $installedLang->key();
            }
        }

        self::$currentLanguage = $defaultLang;

    } // init

} // class

